import sys
import time
import dbm  # python 内建的键值对数据库模块
import hashlib  # 哈希算法模块
import uuid  # 唯一标识符模块
from enum import Enum

import uvicorn

from functools import lru_cache  # 缓存装饰器
from fastapi import FastAPI, Request, Response, status, Path, __version__ as fastapi_version
from fastapi.responses import FileResponse, RedirectResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from pydantic import BaseModel
from starlette.responses import HTMLResponse

from sub import grand_son_app  # 导入子应用
from config import settings  # 导入配置

# 导入路由
from router import routers

app = FastAPI(
    docs_url="/mydoc", redoc_url=None, debug=settings.DEBUG_MODE
)  # 关闭redoc文档 /docs 改名为 /mydoc
# 导入路由
app.include_router(routers)
# mount静态文件
app.mount(settings.STATIC_URL, StaticFiles(directory=settings.STATIC_DIR), name=settings.STATIC_NAME)

# 创建模板对象  其他地方使用 request.app.state.templates
app.state.templates = Jinja2Templates(directory=settings.TEMPLATE_DIR)


class Site(BaseModel):
    name: str = "FastAPI开发与部署Site"
    description: str = "FastAPI 是一个现代化的，快速，高效的Python web框架。"


page = {"title": "FastAPI开发与部署", "body": "FastAPI 是一个现代化的，快速，高效的Python web框架。"}


@lru_cache(maxsize=3)  # 进程重启后，缓存失效, 异步函数不能使用缓存
def lru_test(vid: int):
    print("如果显示本信息，就是没有被缓存，且睡眠时间为2秒")
    time.sleep(2)
    data = {"site": Site(), "page": page, "id": vid}
    return data


@app.get("/post/{vid}")
async def index(request: Request, vid: int):
    # data = {
    #     "request": request,
    #     "site": Site(),
    #     "page": page,
    #     "id": 1
    # }
    data = lru_test(vid)
    print(lru_test.cache_info())  # 打印缓存信息 (hits, misses, maxsize, currsize) 最少最近使用原则
    # 强制清理缓存  lru_test.cache_clear()

    data["request"] = request
    return request.app.state.templates.TemplateResponse("post.html", context=data)


# 路径变量
# 预设值变量
class TypeName(str, Enum):
    blog: str = "blog"
    comment: str = "comment"
    page: str = "page"


@app.get("/path/{type_name}")
async def post_type(
    request: Request,
    type_name: TypeName = Path(title="模块类型", description="可用模块类型:blog、comment、page"),
):
    data = None
    if type_name == TypeName.blog:
        data = "blog模块"
    if type_name == TypeName.comment:
        data = "comment留言模块"
    if type_name == TypeName.page:
        data = "单页面page模块"
    return {"module": data}


# 包含路径的变量
@app.get("/path/{file_path:path}")
async def post_filepath(file_path: str):
    return {"file_path": file_path}


# # 简易端链接
# class PostItem(BaseModel):
#     original_url: str


# # 根据原始链接生成短链接
# @app.post('/short')
# async def shorten_url(url: PostItem):
#     short_url = short_random(original_url=url.original_url)
#     store_short_url(short_url=short_url, original_url=url.original_url)
#     return {"short_url": short_url}


# # 短链接访问
# @app.get('/short/{short_url}')
# async def redirect_url(short_url: str):
#     url = get_original_url(short_url)
#     # return {"original_url": url}
#     return RedirectResponse(f"https://{url}")


# def short_random(original_url: str, length: int = 8) -> str:
#     # 哈希算法生成短链接
#     md5 = hashlib.md5()
#     md5.update(original_url.encode())
#     short_url = md5.hexdigest()[:length]
#     return short_url


# # 短链接存储
# def store_short_url(short_url: str, original_url: str) -> None:
#     with dbm.open(settings.SHORT_URL_DB, 'c') as db:
#         db[short_url] = original_url.encode("utf-8")


# def get_original_url(short_url: str) -> str:
#     with dbm.open(settings.SHORT_URL_DB, 'c') as db:
#         original_url = db.get(short_url, None)
#     return original_url.decode("utf-8")


@app.get("/videos/{video_id}")
async def videos(request: Request, video_id: int):
    if video_id != 1:
        video_id = None
    data = {"request": request, "site": Site(), "id": video_id}
    return request.app.state.templates.TemplateResponse("videos.html", context=data)


@app.get("/file/{video_id}")
async def play(request: Request, video_id: int):
    if video_id != 1:
        return HTMLResponse(status_code=status.HTTP_404_NOT_FOUND, content="视频不存在")
    return FileResponse(f"static/video/{video_id}.mp4")


# mount子应用
son_app = FastAPI()


# 子应用接口
@son_app.get("/")
async def son_api():
    return {"data": "son子应用接口"}


app.mount("/son", son_app, name="son")  # 子应用挂载到 /son 路径下
son_app.mount("/grand_son", grand_son_app, name="grand_son")  # 孙应用挂载到 /son/grand_son 路径下


@app.get("/server-status", include_in_schema=False)  # docs文档显示时，隐藏此接口
async def run_state(response: Response, token: str | None = None):
    if token == "runler":  # 验证系统状态的key
        data = {
            "server_status": "正常运行中",
            "fastapi_version": fastapi_version,
            "python_version": sys.version_info,
        }
        return data
    else:
        response.status_code = status.HTTP_404_NOT_FOUND
        return {"detail": "Not found"}


# favicon.ico 图标
@app.get("/favicon.ico", include_in_schema=False)
async def favicon():
    return FileResponse("static/icon/favicon.ico")


# 向客户端返回URL信息
@app.get("/resources/path/{file}")
@app.post("/resources/path/{file}")
# @app.put("/resources/path/{file}")
# @app.delete("/resources/path/{file}")
async def http_url(*, request: Request, key1, key2):
    response = {
        "协议名称": request.url.scheme,
        "域名": request.url.hostname,
        "端口": request.url.port,
        "路径参数": request.url.path,
        "查询参数": request.url.query,
        "key1的值": key1,
        "key2的值": key2,
        "请求头": request.headers,
        "请求体": await request.body(),
    }
    return response


# cmd 命令行运行：uvicorn main:app --reload --port 8080 --host 127.0.0.1
if __name__ == "__main__":
    uvicorn.run("main:app", reload=True, host="127.0.0.1", port=8080)
